/*
 * Copyright (c) 1999 2000
 * Information Promotion Agency of Japan (IPA),
 * Fujitsu Limited, and 
 * Matsuoka Laboratory, Tokyo Institute of Technology
 * All rights reserved.
 *
 * Redistribution and non-commercial use in source and binary forms, 
 * with or without modification, are permitted provided that the following 
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. All advertising materials mentioning features or use of this 
 *    software must display the following acknowledgement:
 *
 *    This product includes software developed by Information Promotion Agency
 *    of Japan, Fujitsu Limited, and Matsuoka Laboratory, 
 *    Tokyo Institute of Technology.
 *
 * 4. The names "Information Promotion Agency", "Fujitsu Limited",
 *    "Matsuoka Laboratory", or "Tokyo Institute of Technology" should
 *    not be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 5. Any use of the source code or the binary in a commercial product, 
 *    whether may it be the origial representation or in some modified form,
 *    is not permitted without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Revision: 1.49 $
 * $Date: 2000/05/22 09:40:40 $
 * $Author: kouya $
 *
 * Runtime routines for JIT compiled code
 */

#include <alloca.h>
#include <signal.h>
#include <ucontext.h>
#include <sys/types.h>
#include <sys/trap.h>

#include "OpenJIT.h"
#include "org_OpenJIT_Sparc.h"

#ifdef NATIVE_THREAD
#include <thread.h>
#endif

static ClassClass *classJavaLangObject;

#if JDK_VERSION >= 12
static sys_mon_t *_linkclass_lock;
#endif

/* start of SPARC machine dependent definition */
#define R_G3			org_OpenJIT_Sparc_REG_G3
#define R_O0			org_OpenJIT_Sparc_REG_O0
#define R_O1			org_OpenJIT_Sparc_REG_O1
#define R_F0			org_OpenJIT_Sparc_REG_F0
#define R_SP			org_OpenJIT_Sparc_REG_SP
#define R_O7			org_OpenJIT_Sparc_REG_O7
#define R_L0			org_OpenJIT_Sparc_REG_L0
#define R_I0			org_OpenJIT_Sparc_REG_I0

#define Imm(imm)		(((imm)&0x1fff)|(1<<13))
#define HI_INT(X)		(((code_t)(X) & 0xfffff000))
#define LO_INT(X)		((((code_t)(X) & 0x00000fff))|(1<<13))
#define SparcRD(rd)		(((rd)&31)<<25)
#define SparcRS1(rs1)		(((rs1)&31)<<14)
#define SparcOP(op,rd,rs1,rs2)	((op)|SparcRD(rd)|SparcRS1(rs1)|rs2)
#define SparcCALL(disp)		(0x40000000|((code_t)(disp)>>2))
#define SparcSETHI(rd,imm)	((4<<22)|SparcRD(rd)|(((code_t)((imm) & 0xfffffc00) >> 10)))
#define SparcSETLO(rd,imm)	((org_OpenJIT_Sparc_I_OR)|SparcRD(rd)|SparcRS1(rd)|(((imm)&0xfff)|(1<<13)))
#define SparcSPINLOCK(imm)	(org_OpenJIT_Sparc_I_BA|org_OpenJIT_Sparc_I_ANNUL|(((imm) << 10) >> 10))
#define SparcCHKSTACK		(org_OpenJIT_Sparc_I_LD|SparcRS1(R_SP)|Imm(0))
#define SparcCHKSTACKforJNI	(org_OpenJIT_Sparc_I_LD|SparcRS1(R_SP)|(2))

#define CUR_METHOD_OFFSET	64
#define PARAM_REG_SIZE		6
#define PARAM_REG_OFFSET	17 /* arg0 offset from stack pointer (uint32_t *) */
#define PARAM_REGNO(X)		((X) + R_O0)
#define PARAM_OFFSET(X)		((X) * 4 + 68)
#define PARAM_REGNO_IN(X)	((X) + R_I0)
/* end of SPARC machine dependent definition */

typedef unsigned int	code_t;

/*
 * for long(64bit) operation
 */

typedef union {
    int64_t J;
    struct {
	unsigned hi,lo;
    } I;
} Junion;

typedef union {
    double D;
    struct {
	float hi,lo;
    } F;
} Dunion;

static void handle_exception (ExecEnv *) __attribute__ ((noreturn));
static void OpenJIT_Error(char *, char *) __attribute__ ((noreturn));

#define ASM __asm__ volatile
register uint32_t *caller_code __asm__ ("%i7");
register uint32_t *stack_pointer __asm__ ("%sp");
register uint32_t *frame_pointer __asm__ ("%fp");

/* Some JNI methods(ex. readBytes) use the large native stack area for */
/* temporary buffer(BUF_SIZE=8192). If the StackOverFlowError occurs */
/* in such a method, the error can not be handled and causes entire crash. */
/* To prevent this, All Native method must have enough stack space. */
/* Is it enough? */
#define JNI_STACK_REDZONE	-(8192 + 0x1000)

/* The following code must generate "movl %eax,JNI_STACK_REDZONE(%ebp)" */
/* unless, StackOverFlowException can't be catched properly */
#define CHECK_STACK_OVERFLOW()				\
    ASM("sethi %%hi(%0),%%g2" : : "i"(JNI_STACK_REDZONE));	\
    ASM("ld [%sp+%g2],%g0")

#define GET_CALLER_MB(X) \
    X = (struct methodblock *)(frame_pointer[CUR_METHOD_OFFSET>>2])

/* This macro save current executing method on Java Frame. */
/* Some native method(ex. fillInStackTrace) refer it. */
/* And when exception occurs, it would be refered. */
#define SET_COMPILED_FRAME(EE, MB)				\
    do {							\
	struct javaframe *frame = EE->current_frame;		\
	frame->current_method = MB;				\
	frame->vars = (stack_item *)(&frame_pointer[8]);	\
    } while(0)

#define GET_MB(mb)	ASM("mov %%g3,%0" : "=r"(mb) :)
#define GET_IDX(index)	ASM("mov %%g3,%0" : "=r"(index) :)

/* SPARC uses r6 registers as argument. */
#define REG_ARGS	void *arg0, void *arg1, void *arg2, void *arg3, void *arg4, void *arg5

#define ASM_PASS_ARGS(I0)			\
    do {					\
	ASM("mov %0, %%g3" : : "r" (I0));	\
	ASM("mov %0, %%i0" : : "r" (arg0));	\
	ASM("mov %0, %%i1" : : "r" (arg1));	\
	ASM("mov %0, %%i2" : : "r" (arg2));	\
	ASM("mov %0, %%i3" : : "r" (arg3));	\
	ASM("mov %0, %%i4" : : "r" (arg4));	\
	ASM("mov %0, %%i5" : : "r" (arg5));	\
    } while(0)

#define JUMP_IN_COMPILED_CODE(MB)		\
    do {					\
	register uint32_t g1 __asm__ ("%g1");	\
	g1 = (uint32_t)mb->CompiledCode;	\
	ASM_PASS_ARGS(mb);			\
	ASM("jmp %0; restore" : : "r"(g1));	\
	/* never comes here */			\
    } while(0)

#define RESOLVE_NATIVE_OR_COMPILE(MB, CALLER_MB)		\
    if (!ENTRY_IS_FIXED(MB)) {					\
        if (MB->fb.access & ACC_NATIVE) {			\
	    SET_COMPILED_FRAME(EE(), CALLER_MB);		\
	    OpenJIT_resolveNativeMethod(MB);			\
	} else if (MB->fb.access & ACC_ABSTRACT) {		\
	    char buf[256];					\
	    jio_snprintf(buf, sizeof(buf),			\
			 "%s.%s",				\
			 cbName(MB->fb.clazz),			\
			 MB->fb.name);				\
	    OpenJIT_Error(JAVAPKG "AbstractMethodError", buf);	\
	} else if (!COMPILE_ON_THE_WAY(MB)) {			\
	    OpenJIT_compile(EE(), MB);				\
	}							\
    }

#define PATCH_CODE(CODE, OFFSET)				\
    do {							\
	ASM("st %0, [%%i7 + %1]" : : "r"(CODE), "i"(OFFSET));	\
	ASM("iflush %%i7 + %0" : : "i"(OFFSET));		\
    } while(0)

#define PATCH_CODE_CALL(FUNC, OFFSET)					\
    do {								\
	code_t code;							\
	code = SparcCALL((int)(FUNC) - (int)caller_code - OFFSET);	\
	PATCH_CODE(code, OFFSET);					\
    } while(0)

#define PATCH_SET_O0(IMM)		\
    do {					\
	unsigned int val = (unsigned int)(IMM);	\
	PATCH_CODE(SparcSPINLOCK(0), 0);	\
	PATCH_CODE(SparcSETLO(R_O0, val), 4);	\
	/* unlocked */				\
	PATCH_CODE(SparcSETHI(R_O0, val), 0);	\
    } while(0)

#define PATCH_SET_O0_and_CALL(FUNC, IMM)	\
    do {					\
	unsigned int val = (unsigned int)(IMM);	\
	PATCH_CODE(SparcSPINLOCK(0), 0);	\
	PATCH_CODE(SparcSETLO(R_O0, val), 8);	\
	PATCH_CODE_CALL(FUNC, 4);		\
	/* unlocked */				\
	PATCH_CODE(SparcSETHI(R_O0, val), 0);	\
	ASM("add %i7,4,%i7");			\
    } while(0)

#define PATCH_SET_G3_and_CALL(FUNC, IMM)	\
    do {					\
	unsigned int val = (unsigned int)(IMM);	\
	PATCH_CODE(SparcSPINLOCK(0), 0);	\
	PATCH_CODE(SparcSETLO(R_G3, val), 8);	\
	PATCH_CODE_CALL(FUNC, 4);		\
	/* unlocked */				\
	PATCH_CODE(SparcSETHI(R_G3, val), 0);	\
	ASM("add %i7,4,%i7");			\
    } while(0)

#define CHECK_SELF_MODIFYING()					\
    do {							\
	if (caller_code[0] == SparcSPINLOCK(0) ||		\
	    (caller_code[1] >> 30) != 0 /* sethi */) {		\
	    /* in the middle of self modifying. retry */	\
	    ASM("jmp %i7; restore");				\
	}							\
    } while(0)

#if JDK_VERSION < 12
#define RESOLVE_CLASS_CONST(CP, INDEX, TYPE, MB)			     \
    do {								     \
	unsigned char *type_table = CP[CONSTANT_POOL_TYPE_TABLE_INDEX].type; \
	if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, INDEX)) {	     \
	    ExecEnv *ee = EE();						     \
	    SET_COMPILED_FRAME(ee, MB);					     \
	    ResolveClassConstant(constant_pool, INDEX, ee, 1 << TYPE);	     \
	    if (exceptionOccurred(ee)) {				     \
		handle_exception(ee);					     \
	    }								     \
	}								     \
    } while(0)
#else /* JDK_VERSION >= 12 */
#define RESOLVE_CLASS_CONST(CP, INDEX, TYPE, MB)			     \
    do {								     \
	ExecEnv *ee;							     \
	unsigned char *type_table = CP[CONSTANT_POOL_TYPE_TABLE_INDEX].type; \
	if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, INDEX)) {	     \
	    ee = EE();							     \
	    SET_COMPILED_FRAME(ee, MB);					     \
	    ResolveClassConstant(CP, INDEX, ee, 1 << TYPE);		     \
	    if (exceptionOccurred(ee)) {				     \
		handle_exception(ee);					     \
	    }								     \
	} else if (TYPE == CONSTANT_Methodref) {			     \
	    ee = EE();							     \
	    if (!CCIs(CP[INDEX].mb->fb.clazz, Initialized)) {		     \
		if (CP[INDEX].mb->fb.access & ACC_STATIC) {		     \
		    InitClass(CP[INDEX].mb->fb.clazz);			     \
		}							     \
	    }								     \
	    if (exceptionOccurred(ee)) {				     \
		handle_exception(ee);					     \
	    }								     \
	} else if (TYPE == CONSTANT_Fieldref) {				     \
	    ee = EE();							     \
	    if (!CCIs(CP[INDEX].fb->clazz, Initialized)) {		     \
		if (CP[INDEX].fb->access & ACC_STATIC) {		     \
		    InitClass(CP[INDEX].fb->clazz);			     \
		}							     \
	    }								     \
	    if (exceptionOccurred(ee)) {				     \
		handle_exception(ee);					     \
	    }								     \
	}								     \
    } while(0)
#endif

#define RESOLVE_CLASS_CLASS(INDEX, QUICKFUNC, CLASS)			      \
    do {								      \
	struct methodblock *caller_mb;					      \
	cp_item_type *constant_pool;					      \
	GET_CALLER_MB(caller_mb);					      \
	constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));	      \
	RESOLVE_CLASS_CONST(constant_pool, INDEX, CONSTANT_Class, caller_mb); \
	CLASS = constant_pool[INDEX].p;					      \
	PATCH_SET_O0_and_CALL(QUICKFUNC, CLASS);			      \
    } while(0)



static char *
slash2dot(char *src, char *dst, int size) {
    char *buf = dst;
    while(--size > 0) {
	char c = *src;
	if (c == '\0') break;
	*dst = (c == '/') ? '.' : c;
	src++;
	dst++;
    }
    *dst = '\0';
    return buf;
}

static bool_t
makeCompiledFrame(ExecEnv *ee, struct methodblock *mb)
{
    int         nlocals = mb->nlocals + 1; /* maximum number of locals */
    JavaFrame   *old_frame = ee->current_frame; /* previous frame */
    stack_item  *optop = old_frame->optop; /* Points to just before args */
    JavaStack   *stack = old_frame->javastack; /* Current stack pointer */
    JavaFrame   *frame = (JavaFrame *)(optop + nlocals); /* tentative  */

    if (frame->ostack + mb->maxstack >= stack->end_data) {
#if JDK_VERSION < 12
	stack_item *copied_optop;
	int args_size;

	if (stack->next != 0) {
	    stack = stack->next;
	} else {
	    if (stack->stack_so_far +
		JAVASTACK_CHUNK_SIZE*sizeof(stack->data[0]) > JavaStackSize) {
		SignalError(ee, JAVAPKG "StackOverflowError", 0);
		return FALSE;
	    }
	    stack = CreateNewJavaStack(ee, stack);
	    if (stack == 0) {
		SignalError(ee, JAVAPKG "OutOfMemoryError", 0);
		return FALSE;
	    } 
	}
	frame = (JavaFrame *)(stack->data + nlocals);
	if (frame->ostack + mb->maxstack >= stack->end_data) {
	    /* Can't represent valid frame in a Java stack chunk */
	    SignalError(ee, JAVAPKG "InternalError", 0);
	    return FALSE;
	}
	args_size = mb->args_size;
	for (copied_optop = stack->data; --args_size >= 0 ; ) {
	    copied_optop[args_size] = optop[args_size];
	}
	optop = copied_optop;
#else
	if (!ExpandJavaStack(ee, &stack, &frame, NULL, mb->args_size, 0, 0)) {
	  return FALSE;
	}
#endif
    }
    frame->javastack = stack;
    frame->prev = old_frame;
    frame->vars = 0;
    frame->optop = frame->ostack;
    frame->current_method = mb; 
    frame->constant_pool = NULL;
    frame->returnpc = mb->code;
    frame->monitor = 0;
    ee->current_frame = frame;
    return TRUE;
}

#define INVOKE_COMPILED_CODE(RET, TYPE)				\
    if (!makeCompiledFrame(ee, mb))				\
	return FALSE;						\
    /* expand stack frame and passing arguments */		\
    if (mb->args_size > PARAM_REG_SIZE) {			\
	int i;							\
	alloca((mb->args_size - PARAM_REG_SIZE) * 4);		\
	for (i = PARAM_REG_SIZE; i < mb->args_size; i++) {	\
	    stack_pointer[i + PARAM_REG_OFFSET] = optop[i].i;	\
	}							\
    }								\
    ASM("mov %0,%%g3" : : "r"(mb));				\
    RET (*(TYPE (*) ())mb->CompiledCode)			\
	(optop[0].h, optop[1].h, optop[2].h, 			\
	 optop[3].h, optop[4].h, optop[5].h)

static bool_t
invokeCompiledCodeV(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
    JavaFrame *old_frame = ee->current_frame;
    stack_item *optop = old_frame->optop;

    INVOKE_COMPILED_CODE(;, void);
    ee->current_frame = old_frame;
    return !(exceptionOccurred(ee));
}

static bool_t
invokeCompiledCodeI(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
    JavaFrame *old_frame = ee->current_frame;
    stack_item *optop = old_frame->optop;

    INVOKE_COMPILED_CODE(optop[0].i = , int);
    ee->current_frame = old_frame;
    old_frame->optop++;
    return !(exceptionOccurred(ee));
}

static bool_t
invokeCompiledCodeJ(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
    JavaFrame *old_frame = ee->current_frame;
    stack_item *optop = old_frame->optop;
    Junion retval;

    INVOKE_COMPILED_CODE(retval.J = , int64_t);
    optop[0].i = retval.I.hi;
    optop[1].i = retval.I.lo;
    ee->current_frame = old_frame;
    old_frame->optop += 2;
    return !(exceptionOccurred(ee));
}

static bool_t
invokeCompiledCodeF(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
    JavaFrame *old_frame = ee->current_frame;
    stack_item *optop = old_frame->optop;

    INVOKE_COMPILED_CODE(optop[0].f = , float);
    ee->current_frame = old_frame;
    old_frame->optop++;
    return !(exceptionOccurred(ee));
}

static bool_t
invokeCompiledCodeD(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
    JavaFrame *old_frame = ee->current_frame;
    stack_item *optop = old_frame->optop;
    Dunion retval;

    INVOKE_COMPILED_CODE(retval.D = , double);
    optop[0].f = retval.F.hi;
    optop[1].f = retval.F.lo;
    ee->current_frame = old_frame;
    old_frame->optop += 2;
    return !(exceptionOccurred(ee));
}

void *
invokeCompiledCodeTable[] = {
    invokeCompiledCodeV,
    invokeCompiledCodeI,
    invokeCompiledCodeJ,
    invokeCompiledCodeF,
    invokeCompiledCodeD,
};


/* start of function to skip stack frame for exceptions */
__asm__("
.section	\".text\"
skip_ex_beg:"
);

#define DISPATCH_NATIVE(OPTOP) 						\
    ExecEnv *ee;							\
    struct methodblock *mb;						\
    struct methodblock *caller_mb;					\
									\
    GET_MB(mb);								\
    GET_CALLER_MB(caller_mb);						\
    ee = EE();								\
    SET_COMPILED_FRAME(ee, caller_mb);					\
    __builtin_saveregs();						\
    OPTOP = (*(stack_item * (*) (stack_item*, ExecEnv*)) mb->code)	\
		((stack_item *)(&arg0), ee);				\
    if (exceptionOccurred(ee))						\
	handle_exception(ee);

static void
dispatchNormNativeV(void *arg0, ...) 
{
    stack_item *optop;
    DISPATCH_NATIVE(optop);
}

static int
dispatchNormNativeI(void *arg0, ...) 
{
    stack_item *optop;
    DISPATCH_NATIVE(optop);
    return optop[-1].i;
}

static int64_t
dispatchNormNativeJ(void *arg0, ...) 
{
    stack_item *optop;
    Junion ret;

    DISPATCH_NATIVE(optop);
    ret.I.hi = optop[-2].i;
    ret.I.lo = optop[-1].i;
    return ret.J;
}

static float
dispatchNormNativeF(void *arg0, ...) 
{
    stack_item *optop;

    DISPATCH_NATIVE(optop);
    return optop[-1].f;
}

static double
dispatchNormNativeD(void *arg0, ...) 
{
    stack_item *optop;
    Dunion ret;

    DISPATCH_NATIVE(optop);
    ret.F.hi = optop[-2].f;
    ret.F.lo = optop[-1].f;
    return ret.D;
}

static void *
dispatchNormNativeTable[] = {
    dispatchNormNativeV,
    dispatchNormNativeI,
    dispatchNormNativeJ,
    dispatchNormNativeF,
    dispatchNormNativeD,
};

static void
dispatchSyncNative(void *arg0, ...) 
{
    stack_item *optop;
    ExecEnv *ee;
    struct methodblock *mb;
    struct methodblock *caller_mb;
    unsigned int key;

    GET_MB(mb);
    GET_CALLER_MB(caller_mb);
    ee = EE();
    SET_COMPILED_FRAME(ee, caller_mb);
    if (mb->fb.access & ACC_STATIC) {
	key = obj_monitor(cbHandle(fieldclass(&mb->fb)));
    } else {
	key = obj_monitor(arg0);
    }
    monitorEnter(key);
    __builtin_saveregs();
    optop = (*(stack_item * (*) (stack_item*, ExecEnv*)) mb->code)
		((stack_item *)(&arg0), ee);
    monitorExit(key);
    if (exceptionOccurred(ee))
	handle_exception(ee);
    switch(RETURN_TYPE(mb)) {
    case CCF_RETURN_VOID:
	return;
    case CCF_RETURN_INT:
	ASM("ld %0,%%i0" : : "m"(optop[-1].i));
	return;
    case CCF_RETURN_LONG:
	ASM("ld %0,%%i0" : : "m"(optop[-2].i) : "%i0");
	ASM("ld %0,%%i1" : : "m"(optop[-1].i));
	return;
    case CCF_RETURN_FLOAT:
	ASM("ld %0,%%f0" : : "m"(optop[-1].f));
	return;
    case CCF_RETURN_DOUBLE:
	ASM("ld %0,%%f0" : : "m"(optop[-2].f));
	ASM("ld %0,%%f1" : : "m"(optop[-1].f));
	return;
    }
}

#if JDK_VERSION < 12
static int64_t
dispatchJNINativeMethod(void *arg0, ...)
{
    ExecEnv *ee;
    struct methodblock *caller_mb;
    stack_item *optop;
    struct JNIEnv_ *env;
    int last_base, last_top;
    JNIRefTable *localRefs;
    int argc;
    struct methodblock *mb;
    char *sig;
    jref this_or_clazz;
    int64_t ret;

    GET_MB(mb);
    GET_CALLER_MB(caller_mb);
    ee = EE();
    SET_COMPILED_FRAME(ee, caller_mb);

    env = &(ee->nativeInterface);
    localRefs = JNIEnvGetLocalRefs(env);
    last_base = localRefs->base;
    last_top = localRefs->top;
    localRefs->base = localRefs->top;

    __builtin_saveregs();
    optop = (stack_item *)(&arg0);        

    if (mb->fb.access & ACC_STATIC) {
        this_or_clazz = MkRefLocal(env, cbHandle(fieldclass(&mb->fb)), JNI_REF_HANDLE_TAG);
    } else {
	this_or_clazz = MkRefLocal(env, arg0, JNI_REF_HANDLE_TAG);
	++optop;
    }
    for (argc = 0, sig = mb->fb.signature; *(++sig) != SIGNATURE_ENDFUNC; argc++) {
        switch (*sig) {
	case SIGNATURE_CLASS:
	    while(*sig != SIGNATURE_ENDCLASS) sig++;
	    optop[argc].p = (void *)MkRefLocal(env, optop[argc].p, JNI_REF_HANDLE_TAG);
	    break;
	case SIGNATURE_ARRAY:
	    while(*sig == SIGNATURE_ARRAY) sig++;
	    if (*sig == SIGNATURE_CLASS) 
	        while(*sig != SIGNATURE_ENDCLASS) sig++;
	    optop[argc].p = (void *)MkRefLocal(env, optop[argc].p, JNI_REF_HANDLE_TAG);
	    break;
	case SIGNATURE_LONG:
	case SIGNATURE_DOUBLE:
	    argc++;
	}
    }
    if (mb->args_size > PARAM_REG_SIZE - 2) {
	/* expand stack frame for arguments passed via memory */
	alloca((mb->args_size - PARAM_REG_SIZE + 2) * 4);
    }
    for (argc = PARAM_REG_SIZE - 2; argc < mb->args_size; argc++) {
	stack_pointer[argc + 2 + PARAM_REG_OFFSET] = optop[argc].i;
    }
    ret = (*(int64_t (*) ())(mb->code))(env, this_or_clazz, 
		optop[0].p, optop[1].p, optop[2].p, optop[3].p);
    /* register %f0 & %f1 must be preserved from here until return */;

    localRefs->base = last_base;
    localRefs->top = last_top;

    if (exceptionOccurred(ee))
	handle_exception(ee);

    sig++;
    switch (*sig) {
	Junion retval;

    case SIGNATURE_BOOLEAN:
	retval.J = ret;
	retval.I.hi = (jboolean)retval.I.hi;
	return retval.J;
    case SIGNATURE_BYTE:
	retval.J = ret;
	retval.I.hi = (jbyte)retval.I.hi;
	return retval.J;
    case SIGNATURE_SHORT:
	retval.J = ret;
	retval.I.hi = (jshort)retval.I.hi;
	return retval.J;
    case SIGNATURE_CHAR:
	retval.J = ret;
	retval.I.hi = (jchar)retval.I.hi;
	return retval.J;
    case SIGNATURE_CLASS:
    case SIGNATURE_ARRAY:
	retval.J = ret;
	retval.I.hi = (unsigned)DeRef(env, retval.I.hi);
	return retval.J;
    case SIGNATURE_FLOAT:
    case SIGNATURE_DOUBLE:
	/* %f0 & %f1 must be preserved here */
    case SIGNATURE_LONG:
    default:
	return ret;
    }
}

static int64_t
dispatchJNISynchronizedNativeMethod(void *arg0, ...)
{
    ExecEnv *ee;
    struct methodblock *caller_mb;
    stack_item *optop;
    struct JNIEnv_ *env;
    int last_base, last_top;
    JNIRefTable *localRefs;
    int argc;
    struct methodblock *mb;
    char *sig;
    jref this_or_clazz;
    int64_t ret;
    JHandle *o;

    GET_MB(mb);
    GET_CALLER_MB(caller_mb);
    ee = EE();
    SET_COMPILED_FRAME(ee, caller_mb);

    env = &(ee->nativeInterface);
    localRefs = JNIEnvGetLocalRefs(env);
    last_base = localRefs->base;
    last_top = localRefs->top;
    localRefs->base = localRefs->top;

    __builtin_saveregs();
    optop = (stack_item *)(&arg0);        

    if (mb->fb.access & ACC_STATIC) {
        o = (JHandle *)cbHandle(fieldclass(&mb->fb));
    } else {
	o = arg0;
	++optop;
    }
    this_or_clazz = MkRefLocal(env, o, JNI_REF_HANDLE_TAG);
    for (argc = 0, sig = mb->fb.signature; *(++sig) != SIGNATURE_ENDFUNC; argc++) {
        switch (*sig) {
	case SIGNATURE_CLASS:
	    while(*sig != SIGNATURE_ENDCLASS) sig++;
	    optop[argc].p = (void *)MkRefLocal(env, optop[argc].p, JNI_REF_HANDLE_TAG);
	    break;
	case SIGNATURE_ARRAY:
	    while(*sig == SIGNATURE_ARRAY) sig++;
	    if (*sig == SIGNATURE_CLASS) 
	        while(*sig != SIGNATURE_ENDCLASS) sig++;
	    optop[argc].p = (void *)MkRefLocal(env, optop[argc].p, JNI_REF_HANDLE_TAG);
	    break;
	case SIGNATURE_LONG:
	case SIGNATURE_DOUBLE:
	    argc++;
	}
    }
    if (mb->args_size > PARAM_REG_SIZE - 2) {
	/* expand stack frame for arguments passed via memory */
	alloca((mb->args_size - PARAM_REG_SIZE + 2) * 4);
    }
    for (argc = PARAM_REG_SIZE - 2; argc < mb->args_size; argc++) {
	stack_pointer[argc + 2 + PARAM_REG_OFFSET] = optop[argc].i;
    }
    monitorEnter(obj_monitor(o));
    ret = (*(int64_t (*) ())(mb->code))(env, this_or_clazz, 
		optop[0].p, optop[1].p, optop[2].p, optop[3].p);
    /* register %f0 & %f1 must be preserved from here until return */;
    monitorExit(obj_monitor(o));

    localRefs->base = last_base;
    localRefs->top = last_top;

    if (exceptionOccurred(ee))
	handle_exception(ee);

    sig++;
    switch (*sig) {
	Junion retval;

    case SIGNATURE_BOOLEAN:
	retval.J = ret;
	retval.I.hi = (jboolean)retval.I.hi;
	return retval.J;
    case SIGNATURE_BYTE:
	retval.J = ret;
	retval.I.hi = (jbyte)retval.I.hi;
	return retval.J;
    case SIGNATURE_SHORT:
	retval.J = ret;
	retval.I.hi = (jshort)retval.I.hi;
	return retval.J;
    case SIGNATURE_CHAR:
	retval.J = ret;
	retval.I.hi = (jchar)retval.I.hi;
	return retval.J;
    case SIGNATURE_CLASS:
    case SIGNATURE_ARRAY:
	retval.J = ret;
	retval.I.hi = (unsigned)DeRef(env, retval.I.hi);
	return retval.J;
    case SIGNATURE_FLOAT:
    case SIGNATURE_DOUBLE:
	/* %f0 & %f1 must be preserved here */
    case SIGNATURE_LONG:
    default:
	return ret;
    }
}
#else
static int64_t
dispatchJNINativeMethod(void *arg0, ...)
{
    ExecEnv *ee;
    struct methodblock *mb;
    char *sig;
    struct methodblock *caller_mb;
    JavaFrame  *old_frame;	/* previous frame */
    stack_item *optop;		/* Points to just before args */
    JavaStack  *stack;
    JavaFrame  *frame;
    JHandle **this_or_clazz;
    int argc;
    int64_t ret;

    CHECK_STACK_OVERFLOW();
      
    GET_MB(mb);
    GET_CALLER_MB(caller_mb);
    ee = EE();
    old_frame = ee->current_frame;
    SET_COMPILED_FRAME(ee, caller_mb);
    stack = old_frame->javastack; 
    frame = (JavaFrame *)(old_frame->optop + mb->args_size);

    if (frame->ostack + JNI_REF_INFO_SIZE + JNI_DEFAULT_LOCAL_CAPACITY >= 
	stack->end_data) {
        JavaStack *tmp_stack = stack;
	JavaFrame *tmp_frame = frame;
	if (!ExpandJavaStackForJNI(ee, &tmp_stack, &tmp_frame,
		JNI_REF_INFO_SIZE + JNI_DEFAULT_LOCAL_CAPACITY))
	    handle_exception(ee);
	stack = tmp_stack;
	frame = tmp_frame;
    }

    frame->javastack = stack;
    frame->prev = old_frame;
    frame->optop = frame->ostack + JNI_REF_INFO_SIZE;
    frame->current_method = mb;

    JNI_REFS_FREELIST(frame) = NULL;
    JNI_N_REFS_IN_USE(frame) = 0;
    JNI_REFS_CAPACITY(frame) = JNI_DEFAULT_LOCAL_CAPACITY;

    ee->current_frame = frame;

    __builtin_saveregs();
    optop = (stack_item *)&arg0;

    if (mb->fb.access & ACC_STATIC) {
	this_or_clazz = (JHandle **)(&cbHandle(fieldclass(&mb->fb)));
    } else {
	this_or_clazz = (JHandle **)&arg0;
	++optop;
    }

    if (mb->args_size > PARAM_REG_SIZE - 2) {
	/* expand stack frame for arguments passed via memory */
	alloca((mb->args_size - PARAM_REG_SIZE + 2) * 4);
    }
    sig = mb->terse_signature;
    for (argc = 0; *sig != TERSE_SIG_ENDFUNC; argc++, sig++) {
	stack_pointer[argc + 2 + PARAM_REG_OFFSET] = 
	    (*sig == TERSE_SIG_OBJECT) ?
	    (uint32_t)((optop[argc].p == NULL) ? NULL : &(optop[argc].i)) :
	    optop[argc].i;
	if (*sig == TERSE_SIG_LONG || *sig == TERSE_SIG_DOUBLE) {
	    argc++;
	    stack_pointer[argc + 2 + PARAM_REG_OFFSET] = optop[argc].i;
	}
    }
    sig++;
    ret = (*(int64_t (*) ())(mb->code))(EE2JNIEnv(ee), this_or_clazz, 
	stack_pointer[2 + PARAM_REG_OFFSET],
	stack_pointer[3 + PARAM_REG_OFFSET],
	stack_pointer[4 + PARAM_REG_OFFSET],
	stack_pointer[5 + PARAM_REG_OFFSET]);
    /* register %f0 & %f1 must be preserved from here until return */
    ee->current_frame = old_frame;
    if (exceptionOccurred(ee))
	handle_exception(ee);
    if (*sig == TERSE_SIG_OBJECT) {
	Junion retval;
	retval.J = ret;
	retval.I.hi = (unsigned)DeRef(EE2JNIEnv(ee), retval.I.hi);
	return retval.J;
    }
    return ret;
}

static int64_t
dispatchJNISynchronizedNativeMethod(void *arg0, ...)
{
    ExecEnv *ee;
    struct methodblock *mb;
    char *sig;
    struct methodblock *caller_mb;
    JavaFrame  *old_frame;	/* previous frame */
    stack_item *optop;		/* Points to just before args */
    JavaStack  *stack;
    JavaFrame  *frame;
    JHandle **this_or_clazz;
    int argc;
    int64_t ret;
      
    CHECK_STACK_OVERFLOW();
      
    GET_MB(mb);
    GET_CALLER_MB(caller_mb);
    ee = EE();
    old_frame = ee->current_frame;
    SET_COMPILED_FRAME(ee, caller_mb);
    stack = old_frame->javastack; 
    frame = (JavaFrame *)(old_frame->optop + mb->args_size);

    if (frame->ostack + JNI_REF_INFO_SIZE + JNI_DEFAULT_LOCAL_CAPACITY >= 
	stack->end_data) {
        JavaStack *tmp_stack = stack;
	JavaFrame *tmp_frame = frame;
	if (!ExpandJavaStackForJNI(ee, &tmp_stack, &tmp_frame,
		JNI_REF_INFO_SIZE + JNI_DEFAULT_LOCAL_CAPACITY))
	    handle_exception(ee);
	stack = tmp_stack;
	frame = tmp_frame;
    }

    frame->javastack = stack;
    frame->prev = old_frame;
    frame->optop = frame->ostack + JNI_REF_INFO_SIZE;
    frame->current_method = mb;

    JNI_REFS_FREELIST(frame) = NULL;
    JNI_N_REFS_IN_USE(frame) = 0;
    JNI_REFS_CAPACITY(frame) = JNI_DEFAULT_LOCAL_CAPACITY;

    ee->current_frame = frame;

    __builtin_saveregs();
    optop = (stack_item *)&arg0;

    if (mb->fb.access & ACC_STATIC) {
	this_or_clazz = (JHandle **)(&cbHandle(fieldclass(&mb->fb)));
    } else {
	this_or_clazz = (JHandle **)&arg0;
	++optop;
    }

    if (mb->args_size > PARAM_REG_SIZE - 2) {
	/* expand stack frame for arguments passed via memory */
	alloca((mb->args_size - PARAM_REG_SIZE + 2) * 4);
    }
    sig = mb->terse_signature;
    for (argc = 0; *sig != TERSE_SIG_ENDFUNC; argc++, sig++) {
	stack_pointer[argc + 2 + PARAM_REG_OFFSET] = 
	    (*sig == TERSE_SIG_OBJECT) ?
	    (uint32_t)((optop[argc].p == NULL) ? NULL : &(optop[argc].i)) :
	    optop[argc].i;
	if (*sig == TERSE_SIG_LONG || *sig == TERSE_SIG_DOUBLE) {
	    argc++;
	    stack_pointer[argc + 2 + PARAM_REG_OFFSET] = optop[argc].i;
	}
    }
    sig++;
    monitorEnter2(ee, obj_monitor(*this_or_clazz));
    ret = (*(int64_t (*) ())(mb->code))(EE2JNIEnv(ee), this_or_clazz, 
	stack_pointer[2 + PARAM_REG_OFFSET],
	stack_pointer[3 + PARAM_REG_OFFSET],
	stack_pointer[4 + PARAM_REG_OFFSET],
	stack_pointer[5 + PARAM_REG_OFFSET]);
    monitorExit2(ee, obj_monitor(*this_or_clazz));
    /* register %f0 & %f1 must be preserved from here until return */
    ee->current_frame = old_frame;
    if (exceptionOccurred(ee))
	handle_exception(ee);
    if (*sig == TERSE_SIG_OBJECT) {
	Junion retval;
	retval.J = ret;
	retval.I.hi = (unsigned)DeRef(EE2JNIEnv(ee), retval.I.hi);
	return retval.J;
    }
    return ret;
}
#endif

long
dispatchJVM(void *arg0, ...)
{
    ExecEnv *ee;
    struct methodblock *caller_mb;
    stack_item *optop;
    void *obj = NULL;
    long result;
    bool_t isStaticCall;
    long hi_word;
    struct methodblock *mb;

    GET_MB(mb);
    GET_CALLER_MB(caller_mb);
    ee = EE();
    SET_COMPILED_FRAME(ee, caller_mb);

    __builtin_saveregs();
    optop = (stack_item *)(&arg0);
    if (mb->fb.access & ACC_STATIC) {
	isStaticCall = TRUE;
    } else {
	isStaticCall = FALSE;
	obj = optop[0].h;
	++optop;
    }
    
    result = do_execute_java_method_vararg(ee, obj, 0, 0, mb, isStaticCall,
					   optop, &hi_word, TRUE);
    if (exceptionOccurred(ee))
	handle_exception(ee);
    switch(RETURN_TYPE(mb)) {
    case CCF_RETURN_LONG:
	/* return 64bit value */
	ASM("mov %0,%%i1" : : "r"(result) : "%i1");
	return hi_word;
    case CCF_RETURN_FLOAT:
	ASM("ld %0,%%f0" : : "m"(result));
	return 0;
    case CCF_RETURN_DOUBLE:
	ASM("ld %0,%%f0" : : "m"(hi_word));
	ASM("ld %0,%%f1" : : "m"(result));
	return 0;
    }
    return result;
}

void dispatchJITCompiler(REG_ARGS)
{
    ExecEnv *ee;
    struct methodblock *mb;

    GET_MB(mb);
    ee = EE();
    if (OpenJIT_compile(ee, mb)) {
	SET_COMPILED_FRAME(ee, mb);
	JUMP_IN_COMPILED_CODE(mb);
    } else {
	ASM_PASS_ARGS(mb);
	ASM("ba dispatchJVM; restore");
    }
}

void
OpenJIT_resolveNativeMethod(struct methodblock *mb)
{
#if JDK_VERSION < 12
    if (mb->code == 0) {
	monitorEnter(obj_monitor(cbHandle(mb->fb.clazz)));
	if (dynoLink(mb)) {
	    mb->invoker = (mb->fb.access & ACC_SYNCHRONIZED)
		? invokeSynchronizedNativeMethod : invokeNativeMethod;
	} else if (dynoLinkJNI(mb)) {
	    mb->invoker = (mb->fb.access & ACC_SYNCHRONIZED)
		? invokeJNISynchronizedNativeMethod : invokeJNINativeMethod;
	}
	monitorExit(obj_monitor(cbHandle(mb->fb.clazz)));
	if (mb->code == 0)
	    OpenJIT_Error(JAVAPKG "UnsatisfiedLinkError", fieldname(&mb->fb));
    }
#else  /* JDK1.2 */
    void *code;
    ExecEnv *ee = EE();
    sys_thread_t *self = EE2SysThread(ee);

    LINKCLASS_LOCK(self);
    code = mb->code;
    LINKCLASS_UNLOCK(self);

    if (code == NULL) {
        uint32_t lib_info;
    	code = dynoLink(mb, &lib_info);
	if (code == NULL) {
	    OpenJIT_Error(JAVAPKG "UnsatisfiedLinkError", fieldname(&mb->fb));
	} else {
	    LINKCLASS_LOCK(self);
	    if (mb->code == NULL) {
	        mb->code = code;
	        if (lib_info != 0) {
		    if (mb->fb.access & ACC_SYNCHRONIZED) {
			mb->invoker = invokeJNISynchronizedNativeMethod;
		    } else {
		        Invoker inv = getCustomInvoker(methodTerseSig(mb));
			if (inv)
			    mb->invoker = inv;
			else
			    mb->invoker = invokeJNINativeMethod;
		    }
		} else {
		    mb->invoker = (mb->fb.access & ACC_SYNCHRONIZED) 
		        ? invokeSynchronizedNativeMethod : invokeNativeMethod;
		}
	    }
	    LINKCLASS_UNLOCK(self);
	}
    }
#endif
    if (mb->fb.access & ACC_SYNCHRONIZED) {
	mb->CompiledCode = (mb->invoker == invokeSynchronizedNativeMethod)
	    ? dispatchSyncNative : (void *)dispatchJNISynchronizedNativeMethod;
    } else {
	mb->CompiledCode = (mb->invoker == invokeNativeMethod)
	    ? dispatchNormNativeTable[RETURN_TYPE(mb)] : dispatchJNINativeMethod;
    }
    FIX_ENTRY(mb);
}

void
dispatchLazyNativeMethod(REG_ARGS)
{
    struct methodblock *mb;
    struct methodblock *caller_mb;

    GET_MB(mb);
    GET_CALLER_MB(caller_mb);
    SET_COMPILED_FRAME(EE(), caller_mb);
    OpenJIT_resolveNativeMethod(mb);
    JUMP_IN_COMPILED_CODE(mb);
}


static void
OpenJIT_getstatic(int index)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;
    struct fieldblock	*fb;
    long addr;

    CHECK_SELF_MODIFYING();
    index >>= 10;
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Fieldref, caller_mb);
    fb = constant_pool[index].p;

    if ((fb->access & ACC_STATIC) == 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(fb)), buf, sizeof(buf));
	len = strlen(buf);
	/* If buffer overflow, quietly truncate */
	(void) jio_snprintf(buf + len, sizeof(buf)-len,
			    ": field %s used to be static",
			    fb->name);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }

    PATCH_CODE(SparcSPINLOCK(0), 0);
    switch(fb->signature[0]) {
    case SIGNATURE_DOUBLE:
	addr = (long)(fb->u.static_address);
	PATCH_CODE(SparcOP(org_OpenJIT_Sparc_I_LDDF, R_F0, R_O0, LO_INT(addr)), 4);
	break;
    case SIGNATURE_LONG:
	addr = (long)(fb->u.static_address);
	PATCH_CODE(SparcOP(org_OpenJIT_Sparc_I_LD, R_O1, R_O0, LO_INT(addr + 4)), 4);
	PATCH_CODE(SparcOP(org_OpenJIT_Sparc_I_LD, R_O0, R_O0, LO_INT(addr)), 8);
	break;
    case SIGNATURE_FLOAT:
	addr = (long)&(fb->u.static_value);
	PATCH_CODE(SparcOP(org_OpenJIT_Sparc_I_LDF, R_F0, R_O0, LO_INT(addr)), 4);
	break;
    default:
	addr = (long)&(fb->u.static_value);
	PATCH_CODE(SparcOP(org_OpenJIT_Sparc_I_LD, R_O0, R_O0, LO_INT(addr)), 4);
    }
    /* unlocked */
    PATCH_CODE(SparcSETHI(R_O0, HI_INT(addr)), 0);
    ASM("jmp %i7; restore");
}

static int
OpenJIT_resolveStaticField(int index)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;
    struct fieldblock	*fb;

    CHECK_SELF_MODIFYING();
    index >>= 10;
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Fieldref, caller_mb);
    fb = constant_pool[index].p;

    if ((fb->access & ACC_STATIC) == 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(fb)), buf, sizeof(buf));
	len = strlen(buf);
	/* If buffer overflow, quietly truncate */
	(void) jio_snprintf(buf + len, sizeof(buf)-len,
			    ": field %s used to be static",
			    fb->name);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }

    if (fb->signature[0] == SIGNATURE_LONG || fb->signature[0] == SIGNATURE_DOUBLE) {
	PATCH_SET_O0(fb->u.static_address);
	return (int)(fb->u.static_address);
    } else {
	PATCH_SET_O0(&fb->u.static_address);
	return (int)(&fb->u.static_value);
    }
}

static int
OpenJIT_resolveField(int index)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;
    struct fieldblock	*fb;

    CHECK_SELF_MODIFYING();
    index >>= 10;
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Fieldref, caller_mb);
    fb = constant_pool[index].p;

    if (fb->access & ACC_STATIC) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(fb)), buf, sizeof(buf));
	len = strlen(buf);
	/* If buffer overflow, quietly truncate */
	(void) jio_snprintf(buf + len, sizeof(buf)-len,
			    ": field %s did not used to be static",
			    fb->name);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }
    PATCH_SET_O0(fb->u.offset);
    return fb->u.offset;
}

static int
OpenJIT_resolveString(int index)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;

    CHECK_SELF_MODIFYING();
    index >>= 10;
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_String, caller_mb);
    PATCH_SET_O0(constant_pool[index].i);
    return constant_pool[index].i;
}

static HObject *
OpenJIT_new_quick(ClassClass *cb)
{
    HObject *ret;
    if ((ret = ObjAlloc(cb, 0)) == 0) {
	char buf[256];
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", slash2dot(cbName(cb), buf, sizeof(buf)));
    }
    return ret;
}

static HObject * 
OpenJIT_new(int index)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;
    ClassClass		*cb, *current_class;
    HObject 		*ret;

    CHECK_SELF_MODIFYING();
    index >>= 10;
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Class, caller_mb);
    cb = constant_pool[index].p;
#if JDK_VERSION >=12
    InitClass(cb);
#endif
    if (cbAccess(cb) & (ACC_INTERFACE | ACC_ABSTRACT)) {
	OpenJIT_Error(JAVAPKG "InstantiationError", cbName(cb));
    }
    current_class = caller_mb ? fieldclass(&caller_mb->fb) : NULL;
    if (!VerifyClassAccess(current_class, cb, FALSE)) {
	OpenJIT_Error(JAVAPKG "IllegalAccessError", cbName(cb));
    }
    PATCH_SET_O0_and_CALL(OpenJIT_new_quick, cb);
    if ((ret = ObjAlloc(cb, 0)) == 0) {
	char buf[256];
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", slash2dot(cbName(cb), buf, sizeof(buf)));
    }
    return ret;
}

static HArrayOfObject *
OpenJIT_anewarray_quick(ClassClass *array_cb, int size)
{
    HArrayOfObject *ret;

    if (size < 0) {
	OpenJIT_Error(JAVAPKG "NegativeArraySizeException", 0);
    }
    ret = (HArrayOfObject *)ArrayAlloc(T_CLASS, size);
    if (ret == 0) {
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", 0);
    } else {
	unhand(ret)->body[size] = (HObject*)array_cb;
    }
    return ret;
}

static HArrayOfObject *
OpenJIT_anewarray(int index, int size)
{
    ClassClass *array_cb;

    CHECK_SELF_MODIFYING();
    index >>= 10;
    RESOLVE_CLASS_CLASS(index, OpenJIT_anewarray_quick, array_cb);
    return OpenJIT_anewarray_quick(array_cb, size);
}

static HObject *
OpenJIT_multianewarray_quick(ClassClass *array_cb, int dimensions, ...)
{
    va_list ap;
    HObject *ret;
    int i;

    va_start(ap, dimensions);
    for (i = 0; i < dimensions; i++) {
	int dim = va_arg(ap, int);
	if (dim < 0)
	    OpenJIT_Error(JAVAPKG "NegativeArraySizeException", 0);
    }
    va_end(ap);
    ret = MultiArrayAlloc(dimensions, array_cb, (stack_item *)((int)(&dimensions)+4));
    if (ret == 0)
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", 0);
    return ret;
}

static HObject *
OpenJIT_multianewarray(int index, int dimensions, ...)
{
    va_list ap;
    ClassClass *array_cb;
    HObject *ret;
    int i;

    CHECK_SELF_MODIFYING();
    index >>= 10;
    RESOLVE_CLASS_CLASS(index, OpenJIT_multianewarray_quick, array_cb);

    va_start(ap, dimensions);
    for (i = 0; i < dimensions; i++) {
	int dim = va_arg(ap, int);
	if (dim < 0)
	    OpenJIT_Error(JAVAPKG "NegativeArraySizeException", 0);
    }
    va_end(ap);
    ret = MultiArrayAlloc(dimensions, array_cb, (stack_item *)((int)(&dimensions)+4));
    if (ret == 0)
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", 0);
    return ret;
}

static void
OpenJIT_check_aastore(HObject *obj, HArrayOfObject *arr)
{
    ClassClass *cb;
    cb = (ClassClass *)(unhand(arr)->body[obj_length(arr)]);
    if (!IS_INSTANCE_OF(obj, cb, EE())) {
	OpenJIT_Error(JAVAPKG "ArrayStoreException", 0);
    }
}

static void
OpenJIT_checkcast_quick(ClassClass *cb, JHandle *h)
{
    if (!IS_INSTANCE_OF(h, cb, EE())) {
	/* Checkcast fails  */
	char buf[256];
	OpenJIT_Error(JAVAPKG "ClassCastException", 
	      slash2dot(cbName(obj_array_classblock(h)), 
			       buf, sizeof(buf)));
    }
}

static void
OpenJIT_checkcast(int index, JHandle *h)
{
    ClassClass *cb;

    CHECK_SELF_MODIFYING();
    index >>= 10;
    RESOLVE_CLASS_CLASS(index, OpenJIT_checkcast_quick, cb);
    OpenJIT_checkcast_quick(cb, h);
}

static bool_t
OpenJIT_instanceof_quick(ClassClass *cb, JHandle *h)
{
    return ((h != NULL) && IS_INSTANCE_OF(h, cb, EE()));
}

static bool_t
OpenJIT_instanceof(int index, JHandle *h)
{
    ClassClass *cb;

    CHECK_SELF_MODIFYING();
    index >>= 10;
    RESOLVE_CLASS_CLASS(index, OpenJIT_instanceof_quick, cb);
    return ((h != NULL) && IS_INSTANCE_OF(h, cb, EE()));
}

static int
OpenJIT_lcmp(int64_t x, int64_t y)
{
    if (x > y)
	return 1;
    if (x == y)
	return 0;
    else
	return -1;
}

static int
OpenJIT_fcmpl(float l, float r)
{
    return (l < r) ? -1 : (l > r) ? 1 : (l == r ) ?  0 
	/* Beyond here, must be NaN. */
	: -1;
}

static int
OpenJIT_fcmpg(float l, float r)
{
    return (l < r) ? -1 : (l > r) ? 1 : (l == r ) ?  0 
	/* Beyond here, must be NaN. */
	: 1;
}

static int
OpenJIT_dcmpl(double l, double r)
{
    return (l < r) ? -1 : (l > r) ? 1 : (l == r ) ?  0 
	/* Beyond here, must be NaN. */
	: -1;
}

static int
OpenJIT_dcmpg(double l, double r)
{
    return (l < r) ? -1 : (l > r) ? 1 : (l == r ) ?  0 
	/* Beyond here, must be NaN. */
	: 1;
}

static float
OpenJIT_frem(float x, float y)
{
    return (float)fmod((double)x, (double)y);
}

static void
OpenJIT_invokesuper_quick(REG_ARGS)
{
    struct methodblock *caller_mb;
    ClassClass *super_class;
    struct methodblock *mb;

    GET_MB(mb);
    GET_CALLER_MB(caller_mb);
    super_class = cbSuperclass(fieldclass(&caller_mb->fb));

    mb = cbMethodTable(super_class)->methods[mb->fb.u.offset];
    JUMP_IN_COMPILED_CODE(mb);
}

#if JDK_VERSION >= 12
#define isSpecialSuperCall(X, Y) _isSpecialSuperCall(X,Y)
static bool_t
_isSpecialSuperCall(ClassClass *current_class, struct methodblock *mb) {
    if (((cbAccess(current_class) & ACC_SUPER) != 0)
	  && (!IsPrivate(mb->fb.access))
	  && (strcmp(mb->fb.name, "<init>") != 0)
	  && (current_class != classJavaLangObject)) {
	ClassClass *methodClass = fieldclass(&mb->fb);
	ClassClass *cb = cbSuperclass(current_class);
	for (; ; cb = cbSuperclass(cb)) {
	    if (cb == methodClass) 
		return TRUE;
	    if (cbSuperclass(cb) == 0)
		return FALSE;
	}
    }
    return FALSE;
}
#endif

static void
OpenJIT_invokespecial(REG_ARGS)
{
    struct methodblock *caller_mb;
    ClassClass *current_class;
    struct methodblock *mb;

    GET_MB(mb);
    GET_CALLER_MB(caller_mb);
    current_class = caller_mb ? 
	fieldclass(&caller_mb->fb) : NULL;

    if ((mb->fb.access & ACC_STATIC) != 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(&mb->fb)), buf, sizeof(buf));
	len = strlen(buf);
	(void )jio_snprintf(buf + len, sizeof(buf)-len, 
			    ": method %s%s did not used to be static",
			    mb->fb.name, mb->fb.signature);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }
    if ((mb->fb.access & ACC_ABSTRACT) != 0) {
	OpenJIT_Error(JAVAPKG "AbstractMethodError", fieldname(&mb->fb));
    }

    if ((current_class != NULL) && isSpecialSuperCall(current_class, mb)) {
	ClassClass *super_class = cbSuperclass(current_class);
	struct methodblock *new_mb = cbMethodTable(super_class)->methods[mb->fb.u.offset];
	if (mb != new_mb) {
	    PATCH_CODE_CALL(OpenJIT_invokesuper_quick, 0);
	    JUMP_IN_COMPILED_CODE(new_mb);
	}
    }
    RESOLVE_NATIVE_OR_COMPILE(mb, caller_mb);
    if (ENTRY_IS_FIXED(mb))
	PATCH_CODE_CALL(mb->CompiledCode, 0);
    JUMP_IN_COMPILED_CODE(mb);
}

static void
OpenJIT_invokespecial_resolve(REG_ARGS)
{
    unsigned int index;
    struct methodblock *caller_mb;
    ClassClass *current_class;
    struct methodblock *mb;
    cp_item_type *constant_pool;

    CHECK_SELF_MODIFYING();
    GET_IDX(index);
    index >>= 10;
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Methodref, caller_mb);
    mb = constant_pool[index].mb;

    current_class = caller_mb ? 
	fieldclass(&caller_mb->fb) : NULL;

    if ((mb->fb.access & ACC_STATIC) != 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(&mb->fb)), buf, sizeof(buf));
	len = strlen(buf);
	(void )jio_snprintf(buf + len, sizeof(buf)-len, 
			    ": method %s%s did not used to be static",
			    mb->fb.name, mb->fb.signature);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }
    if ((mb->fb.access & ACC_ABSTRACT) != 0) {
	OpenJIT_Error(JAVAPKG "AbstractMethodError", fieldname(&mb->fb));
    }

    if ((current_class != NULL) && isSpecialSuperCall(current_class, mb)) {
	ClassClass *super_class = cbSuperclass(current_class);
	struct methodblock *new_mb = cbMethodTable(super_class)->methods[mb->fb.u.offset];
	if (mb != new_mb) {
	    PATCH_SET_G3_and_CALL(OpenJIT_invokesuper_quick, mb);
	    JUMP_IN_COMPILED_CODE(new_mb);
	}
    }
    RESOLVE_NATIVE_OR_COMPILE(mb, caller_mb);
    if (ENTRY_IS_FIXED(mb))
	PATCH_SET_G3_and_CALL(mb->CompiledCode, mb);
    else
	PATCH_SET_G3_and_CALL(OpenJIT_invokespecial, mb);
    JUMP_IN_COMPILED_CODE(mb);
}

static void define_asm_constants() __attribute__((unused));
static void define_asm_constants() {
    ASM(".off_obj = %0" : :
	"i"(offsetof(JHandle, obj)));
    ASM(".off_methods = %0" : :
	"i"(offsetof(JHandle, methods)));
    ASM(".off_CompiledCode = %0" : :
	"i"(offsetof(struct methodblock, CompiledCode)));
    ASM(".off_invoker = %0" : :
	"i"(offsetof(struct methodblock, invoker)));
    ASM(".off_fb.u.offset = %0" : :
	"i"(offsetof(struct methodblock, fb.u.offset)));
    ASM(".off_methodtable = %0" : :
	"i"(offsetof(struct Classjava_lang_Class, methodtable)));
    ASM(".FLAG_MASK = %0" : :
	"i"(FLAG_MASK));
}

extern void OpenJIT_invokevirtual_quick(REG_ARGS);
__asm__("
	.align 4
	.type	 OpenJIT_invokevirtual_quick,#function
	.proc	020
!
! %g1: (JHandle *)arg0->methods
! %g3: (mb->fb.u.offset << 2) + offsetof(JHandle, methods)
!
OpenJIT_invokevirtual_quick:
	ld [%g1+%g3],%g3
! jump mb->CompiledCode
	ld [%g3+.off_CompiledCode],%g1
	jmp %g1
	nop
	.size	 OpenJIT_invokevirtual_quick,16
");

extern void OpenJIT_invokevirtualobject_quick(REG_ARGS);
__asm__("
	.align 4
	.type	 OpenJIT_invokevirtualobject_quick,#function
	.proc	020
!
! %g1: (JHandle *)arg0->methods
! %g3: (mb->fb.u.offset << 2) + offsetof(JHandle, methods)
!
OpenJIT_invokevirtualobject_quick:
	andcc %g1,.FLAG_MASK,%g0		! T_NORMAL_OBJECT
	be OpenJIT_invokevirtual_quick
	nop
	sethi %hi(classJavaLangObject),%g1
	ld [%g1+%lo(classJavaLangObject)],%g1
	ld [%g1+.off_obj],%g1			! %g1 = unhand(%g1)
	ld [%g1+.off_methodtable],%g1		! %g1 = cbMethodTable(%g1)
	ld [%g1+%g3],%g3
! jump to mb->CompiledCode
	ld [%g3+.off_CompiledCode],%g1
	jmp %g1
	nop
	.size	 OpenJIT_invokevirtualobject_quick,44
");

static void
OpenJIT_invokevirtual_resolve(REG_ARGS)
{
    unsigned int index;
    struct methodblock *mb;
    struct methodblock *caller_mb;
    cp_item_type *constant_pool;

    CHECK_SELF_MODIFYING();
    GET_IDX(index);
    index >>= 10;
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Methodref, caller_mb);
    mb = constant_pool[index].mb;
    if ((mb->fb.access & ACC_STATIC) != 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(&mb->fb)), buf, sizeof(buf));
	len = strlen(buf);
	(void )jio_snprintf(buf + len, sizeof(buf)-len, 
			    ": method %s%s did not used to be static",
			    mb->fb.name, mb->fb.signature);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }
    if ((mb->fb.access & (ACC_PRIVATE | ACC_FINAL)) != 0) {
	RESOLVE_NATIVE_OR_COMPILE(mb, caller_mb);
	if (ENTRY_IS_FIXED(mb))
	    PATCH_SET_G3_and_CALL(mb->CompiledCode, mb);
    } else {
	int offset = (mb->fb.u.offset << 2) + offsetof(JHandle, methods);
	if (fieldclass(&mb->fb) == classJavaLangObject) {
	    PATCH_SET_G3_and_CALL(OpenJIT_invokevirtualobject_quick, offset);
	    mb = mt_slot(obj_array_methodtable((JHandle *)arg0), mb->fb.u.offset);
	} else {
	    PATCH_SET_G3_and_CALL(OpenJIT_invokevirtual_quick, offset);
	    mb = mt_slot(obj_methodtable((JHandle *)arg0), mb->fb.u.offset);
	}
	RESOLVE_NATIVE_OR_COMPILE(mb, caller_mb);
    }
    JUMP_IN_COMPILED_CODE(mb);
}

static void
OpenJIT_invokestatic_resolve(REG_ARGS)
{
    unsigned int index;
    struct methodblock *mb;
    struct methodblock *caller_mb;
    cp_item_type *constant_pool;

    CHECK_SELF_MODIFYING();
    GET_IDX(index);
    index >>= 10;
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Methodref, caller_mb);
    mb = constant_pool[index].mb;

    if ((mb->fb.access & ACC_STATIC) == 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(&mb->fb)), buf, sizeof(buf));
	len = strlen(buf);
	(void )jio_snprintf(buf + len, sizeof(buf)-len, 
			    ": method %s%s used to be static",
			    mb->fb.name, mb->fb.signature);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }

    RESOLVE_NATIVE_OR_COMPILE(mb, caller_mb);
    if (ENTRY_IS_FIXED(mb))
	PATCH_SET_G3_and_CALL(mb->CompiledCode, mb);
    JUMP_IN_COMPILED_CODE(mb);
}

static void
OpenJIT_invokestatic(REG_ARGS)
{
    struct methodblock *mb;
    struct methodblock *caller_mb;

    GET_MB(mb);
    GET_CALLER_MB(caller_mb);

    if ((mb->fb.access & ACC_STATIC) == 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(&mb->fb)), buf, sizeof(buf));
	len = strlen(buf);
	(void )jio_snprintf(buf + len, sizeof(buf)-len, 
			    ": method %s%s used to be static",
			    mb->fb.name, mb->fb.signature);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }

    RESOLVE_NATIVE_OR_COMPILE(mb, caller_mb);
    if (ENTRY_IS_FIXED(mb))
	PATCH_CODE_CALL(mb->CompiledCode, 0);
    JUMP_IN_COMPILED_CODE(mb);
}

static void
OpenJIT_invokeinterface_quick(REG_ARGS)
{
    JHandle *o = arg0;
    struct methodblock *mb;
    struct methodblock *caller_mb;
    cp_item_type *constant_pool;

    struct methodblock *imb;
    ClassClass *intf;
    unsigned offset;
    int guess;
    ClassClass *cb;
    struct methodtable *mtable;
    struct imethodtable *imtable;
    unsigned int index;

    /* Calling sequence is as follows: */
    /*     sethi %hi(guess:index),%g3 */
    /*     call OpenJIT_invokeinterface_quick */
    /*     or %g3,%lo(index),%g3 */

    GET_IDX(index);
    index &= 0xffff;

    /* get previous table index buried in SETHI instruction */
    ASM("ld [%%i7-4],%0" : "=r"(guess));
    guess = (guess >> 6) & 0xffff;

    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    imb = constant_pool[index].mb;
    intf = imb->fb.clazz;
    offset = imb->fb.u.offset;

    mtable = obj_methodtable(o);
    cb = mtable->classdescriptor;
    imtable = cbIntfMethodTable(cb);
    if (guess < (unsigned int)imtable->icount &&
	imtable->itable[guess].classdescriptor == intf)
	goto found_interface;
    for (guess = imtable->icount - 1; guess >= 0; guess--) {
	if (imtable->itable[guess].classdescriptor == intf) {
	    /* bury table index into SETHI instruction */
	    index = (guess << 16) | index;
	    ASM("st %0,[%%i7-4]" : : "r"(SparcSETHI(R_G3, index)));
	    goto found_interface;
	}
    }
    /* not found */
    { 
	char buf[256];
	char *p;

	jio_snprintf(buf, 256, 
		     "class %s does not implement interface %s",
		     cbName(cb), cbName(intf));
	for (p = buf; *p; p++) 
	    if (*p == '/') *p = '.';
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }

found_interface:
    mb = mt_slot(mtable, imtable->itable[guess].offsets[offset]);
    if (mb->CompiledCode == NULL) {
	char buf[256];

	if (mb->fb.name[0] == '+') {
	    jio_snprintf(buf, sizeof(buf), 
			 "access non-public method %s.%s through an interface",
			 cbName(mb->fb.clazz),
			 mb->fb.name + 1);
	    OpenJIT_Error(JAVAPKG "IllegalAccessError", buf);
	} else {
	    jio_snprintf(buf, sizeof(buf), 
			 "%s.%s",
			 cbName(mb->fb.clazz),
			 mb->fb.name);
	    OpenJIT_Error(JAVAPKG "AbstractMethodError", buf);
	}
    }
    JUMP_IN_COMPILED_CODE(mb);
}

static void
OpenJIT_invokeinterface(REG_ARGS)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;
    int index;

    GET_IDX(index);
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_InterfaceMethodref, caller_mb);
    PATCH_CODE_CALL(OpenJIT_invokeinterface_quick, 0);
    ASM_PASS_ARGS(index);
    ASM("call OpenJIT_invokeinterface_quick; restore");
}

static JHandle *
OpenJIT_newarray(int type, int size)
{
    JHandle *ret;

    if (size < 0) {
	OpenJIT_Error(JAVAPKG "NegativeArraySizeException", 0);
    }
    ret = ArrayAlloc(type, size);
    if (ret == 0) {
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", 0);
    }
    return ret;
}

static void
OpenJIT_athrow(Hjava_lang_Object *obj)
{
    ExecEnv *ee = EE();
    struct methodblock *caller_mb;

    if (obj == NULL)
	OpenJIT_Error(JAVAPKG "NullPointerException", 0);
    GET_CALLER_MB(caller_mb);
    SET_COMPILED_FRAME(ee, caller_mb);
    exceptionThrow(ee, obj);
    handle_exception(ee);
}

#if defined(HAVE___MUL64)
extern int64_t __mul64(int64_t x, int64_t y);
#define OpenJIT_lmul	__mul64
#elif defined(HAVE___MULDI3)
extern int64_t __muldi3(int64_t x, int64_t y);
#define OpenJIT_lmul	__muldi3
#else
static int64_t OpenJIT_lmul(int64_t x, int64_t y)
{
  return x * y;
}
#endif

#if defined(HAVE___DIV64)
extern int64_t __div64(int64_t x, int64_t y);
#define OpenJIT_ldiv	__div64
#elif defined(HAVE___DIVDI3)
extern int64_t __divdi3(int64_t x, int64_t y);
#define OpenJIT_ldiv	__divdi3
#else
static int64_t OpenJIT_ldiv(int64_t x, int64_t y)
{
  return x / y;
}
#endif

#if defined(HAVE___REM64)
extern int64_t __rem64(int64_t x, int64_t y);
#define OpenJIT_lrem	__rem64
#elif defined(HAVE___REMDI3)
extern int64_t __remdi3(int64_t x, int64_t y);
#define OpenJIT_lrem	__remdi3
#else
static int64_t OpenJIT_lrem(int64_t x, int64_t y)
{
  return x % y;
}
#endif

#if defined(HAVE___FTOLL)
extern int64_t __ftoll(float f);
#define F2L(F)	__ftoll(F)
#elif defined(HAVE___FIXSFDI)
extern int64_t __fixsfdi(float f);
#define F2L(F)	__fixsfdi(F)
#else
#define F2L(F)	((int64_t)f)
#endif

#if defined(HAVE___DTOLL)
extern int64_t __dtoll(double d);
#define D2L(D)	__dtoll(D)
#elif defined(HAVE___FIXDFDI)
extern int64_t __fixdfdi(float d);
#define D2L(D)	__fixdfdi(D)
#else
#define D2L(D)	((int64_t)d)
#endif

#include <ieeefp.h>

static int64_t OpenJIT_f2l(float f)
{
    if (isnanf(f)) return 0L;
    return F2L(f);
}

static int64_t OpenJIT_d2l(double d)
{
    if (isnand(d)) return 0L;
    return D2L(d);
}

#if JDK_VERSION < 12
static void
OpenJIT_monitorEnter(unsigned key)
{
    if (key == 0)
	OpenJIT_Error(JAVAPKG "NullPointerException", 0);
    monitorEnter(key);
}

static void
OpenJIT_monitorExit(unsigned key)
{
    if (key == 0)
	OpenJIT_Error(JAVAPKG "NullPointerException", 0);
    monitorExit(key);
}
#else
static void
OpenJIT_monitorEnter(unsigned key)
{
    ExecEnv *ee;
    if (key == 0)
	OpenJIT_Error(JAVAPKG "NullPointerException", 0);
    ee = EE();
    monitorEnter2(ee, key);
    if (exceptionOccurred(ee))
	handle_exception(ee);
}

static void
OpenJIT_monitorExit(unsigned key)
{
    ExecEnv *ee;
    if (key == 0)
	OpenJIT_Error(JAVAPKG "NullPointerException", 0);
    ee = EE();
    monitorExit2(ee, key);
    if (exceptionOccurred(ee))
	handle_exception(ee);
}
#endif

#define OpenJIT_drem	fmod

static int64_t
OpenJIT_lshr(signed hi, unsigned lo, unsigned b)
{
    int bm;
    Junion ret;

    if (b != 0) {
	bm = 32 - b;
	if (bm <= 0) {
	    lo = hi >> -bm;
	    hi = hi >> 31;
	} else {
	    unsigned carries = hi << bm;
	    hi = hi >> b;
	    lo = (lo >> b) | carries;
	}
    }
    ret.I.hi = hi;
    ret.I.lo = lo;
    return ret.J;
}

static int64_t
OpenJIT_lushr(unsigned hi, unsigned lo, unsigned b)
{
    int bm;
    Junion ret;

    if (b != 0) {
	bm = 32 - b;
	if (bm <= 0) {
	    lo = hi >> -bm;
	    hi = 0;
	} else {
	    unsigned carries = hi << bm;
	    hi = hi >> b;
	    lo = (lo >> b) | carries;
	}
    }
    ret.I.hi = hi;
    ret.I.lo = lo;
    return ret.J;
}

static int64_t
OpenJIT_lshl(signed hi, unsigned lo, unsigned b)
{
    int bm;
    Junion ret;

    if (b != 0) {
	bm = 32 - b;
	if (bm <= 0) {
	    hi = lo << -bm;
	    lo = 0;
	} else {
	    unsigned carries = lo >> bm;
	    lo = lo << b;
	    hi = (hi << b) | carries;
	}
    }
    ret.I.hi = hi;
    ret.I.lo = lo;
    return ret.J;
}

static float
OpenJIT_l2f(signed hi, unsigned lo)
{
    if (hi < 0) {
	Junion ll;

	ll.I.hi = hi;
	ll.I.lo = lo;
	ll.J = -ll.J;
	return -((float)ll.I.hi * 4294967296.0F + (float)ll.I.lo);
    } else {
	return (float)hi * 4294967296.0F + (float)lo;
    }
}

static double
OpenJIT_l2d(signed hi, unsigned lo)
{
    if (hi < 0) {
	Junion ll;

	ll.I.hi = hi;
	ll.I.lo = lo;
	ll.J = -ll.J;
	return -((double)ll.I.hi * 4294967296.0 + (double)ll.I.lo);
    } else {
	return (double)hi * 4294967296.0 + (double)lo;
    }
}

/* exception and error handling */

/* we assume the following memory mapping */
/*	text segment <  0x0002XXXX */
/*	bss segment  <  0xe0000000 */
/*	shared lib   >= 0xe0000000 */
#ifdef DEBUG
#define PC_IN_COMPILED_CODE(pc) \
    ((unsigned int)(((unsigned int)(pc) >> 16) - 0x0002) < 0xe000 - 0x0002)
#else
/* gcc optimizes loop invariant. but the following macro must not use a register. */
#define PC_IN_COMPILED_CODE(pc) \
    (((unsigned int)(pc) >> 28) < 0xe)
#endif

static unsigned char *
find_handler(ExecEnv *ee, struct methodblock *mb,
		      JHandle *object, uint32_t *pc)
{
    int i;
    struct CatchFrame *cf;
    struct compiled_CatchFrame *ccf;
    cp_item_type *constant_pool = cbConstantPool(fieldclass(&mb->fb));

    for (i = 0; i < mb->exception_table_length; i++) { 
	cf = mb->exception_table + i;
	ccf = cf->compiled_CatchFrame;
	if (ccf->start_pc <= (caddr_t)pc && (caddr_t)pc < ccf->end_pc) { 
	    /* Is the class name a superclass of object's class. */
	    if (cf->catchType == 0) {
		/* The finally case */
		return (unsigned char *)ccf->handler_pc;
	    } else { 
		/* The usual case */
		int catchType = cf->catchType;
		ClassClass *catchClass;
		unsigned char *type_table = constant_pool[CONSTANT_POOL_TYPE_TABLE_INDEX].type;
		JHandle *object = ee->exception.exc;

		exceptionClear(ee);
		if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, catchType)) {
		    if (!ResolveClassConstantFromClass(fieldclass(&mb->fb), catchType, ee, 1 << CONSTANT_Class))
			return 0;
		}
		if (!exceptionOccurred(ee)) {
		    exceptionThrow(ee, object);
		}
		catchClass = constant_pool[catchType].clazz;
		if (is_instance_of(object, catchClass, ee)) {
		    return (unsigned char *)ccf->handler_pc;
		}
	    }
	}
    }
    /* not found */
    return 0;
}

#if JDK_VERSION<12
#define MONITOR_EXIT2(ee, key)	monitorExit(key)
#else  /* JDK1.2 */
/* privatize symbols problem. */
/* gcc's loop invariant optimization causes SEGV. */
/* because handle_exception rewinds register windows. */
#define MONITOR_EXIT2(ee, key)	exception_monitorExit2(ee, key)
/* this function must not be inlined. */
static void
exception_monitorExit2(ExecEnv *ee, unsigned key)
{
    monitorExit2(ee, key);
}
#endif

static void
handle_exception (register ExecEnv *ee)
{
    while(1) {
	/* discard native stack frame of runtime routines */
	/* save ee */
	ASM("mov %0,%%g2" : : "r"(ee));
/*
 *	while (caller_code >= skip_ex_beg && caller_code < skip_ex_end) {
 *	    ASM("restore; ! %0" : "=r"(caller_code));
 *	}
 *
 *	if (!PC_IN_COMPILED_CODE(caller_code)) {
 *	    ASM("ret; restore");
 *	}
 */
	ASM("
	.LL_TEST_SKIP:
		sethi	%hi(skip_ex_beg), %g1
		or	%g1, %lo(skip_ex_beg), %g1
		cmp	%i7, %g1
		bcs	.LL_OUTSIDE_SKIP
		sethi	%hi(skip_ex_end),%g1
		or	%g1, %lo(skip_ex_end), %g1
		cmp	%i7, %g1
		bcc	.LL_OUTSIDE_SKIP
		nop
		restore
		ba,a	.LL_TEST_SKIP
	.LL_OUTSIDE_SKIP:
		srl	 %i7, 0x1c, %g1
		cmp	 %g1, 0xd
		bleu	.LL_PC_IN_COMPILED_CODE
		nop
		ret
		restore
	.LL_PC_IN_COMPILED_CODE:
	");
	/* resoter ee */
	ASM("mov %%g2,%0" : "=r"(ee) :);

	/* JVM on JDK1.1 references lastpc. ??? */
	ee->current_frame->lastpc = (unsigned char *)caller_code;

	{
	    unsigned char *new_pc;
	    struct methodblock *mb;

	    GET_CALLER_MB(mb);
	    new_pc = find_handler(ee, mb, ee->exception.exc, caller_code);
	    if (new_pc != 0) {	/* Found the exception handler. */
		exceptionClear(ee);
		/* optop = object. %i0 is used to push Exception */
		ASM ("mov %0, %%i0" : : "r"(ee->exception.exc) : "%i0");
		/* jump to catch frame */
		ASM ("jmp %0; restore" : : "r" (new_pc));
	    }
	    /* not found in this stack frame */
	    if (mb->fb.access & ACC_SYNCHRONIZED) {
		ASM("restore %0,0,%1" : "=r"(ee) : "r"(ee));
		MONITOR_EXIT2(ee, obj_monitor((JHandle *)frame_pointer[-1]));
	    } else {
		ASM("restore %0,0,%1" : "=r"(ee) : "r"(ee));
	    }
	}
    }
    exit (1);
}

static void
OpenJIT_Error(char *ename, char *DetailMessage)
{
    ExecEnv *ee = EE();
    uint32_t *fp;
    uint32_t caller_pc;

    /* flush register windows */
    ASM("ta 0x3");
    fp = frame_pointer;
    do {
	caller_pc = (uint32_t)(fp[15]);
	fp = (uint32_t *)(fp[14]);
    } while(!PC_IN_COMPILED_CODE(caller_pc));
    ee->current_frame->current_method = (struct methodblock *)fp[16];
    ee->current_frame->vars = (stack_item *)(fp+8);
    SignalError(ee, ename, DetailMessage);
    handle_exception(ee);
    /* never returns here */
}

static void
catchZeroDivide(unsigned char *pc)
{
    /* signal handler on Solaris 2.X breaks %i7(return pc) */
    ASM("mov %0,%%i7" : : "r" (pc));
    OpenJIT_Error(JAVAPKG "ArithmeticException", "/ by zero");
}

static void
catchNullPointer(unsigned char *pc)
{
    /* signal handler on Solaris 2.X breaks %i7(return pc) */
    ASM("mov %0,%%i7" : : "r" (pc));
    OpenJIT_Error(JAVAPKG "NullPointerException", 0);
}

static void
catchStackOverflow(unsigned char *pc)
{
    /* signal handler on Solaris 2.X breaks %i7(return pc) */
    ASM("mov %0,%%i7" : : "r" (pc));
    OpenJIT_Error(JAVAPKG "StackOverflowError", 0);
}

static void
catchArrayIndexOutOfBounds(unsigned char *pc, int index)
{
    char buf[256];

    /* signal handler on Solaris 2.X breaks %i7(return pc) */
    ASM("mov %0,%%i7" : : "r" (pc));
    (void) jio_snprintf(buf, sizeof(buf), "%d", index);
    OpenJIT_Error(JAVAPKG "ArrayIndexOutOfBoundsException", buf);
}

/* end of function to skip stack frame for exceptions */
__asm__("
.section	\".text\"
skip_ex_end:"
);

#if JDK_VERSION < 12
#define SIGNAL_HANDLER_TYPE	void
#define SIGNAL_HANDLER_FAIL
#else /* JDK1.2 */
#define SIGNAL_HANDLER_TYPE	bool_t
#define SIGNAL_HANDLER_FAIL	TRUE
#define setcontext(X)	return TRUE
#endif

static SIGNAL_HANDLER_TYPE
OpenJIT_SignalHandler(int sig, siginfo_t *info, ucontext_t *uc)
{
    int si_code = info->si_code;

#ifdef NATIVE_THREAD
    sigset_t set;

    sigemptyset(&set);
    sigaddset(&set, sig);
    thr_sigsetmask(SIG_UNBLOCK, &set, NULL);
#endif

    switch(sig) {
    case SIGFPE:
	switch (si_code) {
	case FPE_INTDIV:
	case FPE_INTOVF: /* ??? */
	    uc->uc_mcontext.gregs[REG_O0] = (int)info->si_addr;
	    uc->uc_mcontext.gregs[REG_PC] = (int)catchZeroDivide;
	    uc->uc_mcontext.gregs[REG_nPC] = (int)catchZeroDivide + 4;
	    setcontext(uc);
	}
	break;

    case SIGSEGV:
	{
	    unsigned int code = *(unsigned int *)(uc->uc_mcontext.gregs[REG_PC]);
	    int regno = (code >> 14) & 31;
	    int regval;

	    if (regno == 0) {
		regval = 0;
	    } else if (regno <= R_O7) {
		regval = uc->uc_mcontext.gregs[REG_G1+regno-1];
	    } else {
		int *sp = (int *)(uc->uc_mcontext.gregs[REG_O6]);
		regval = sp[regno - R_L0];
	    }
	    if (regval == 0) {
		uc->uc_mcontext.gregs[REG_O0] = uc->uc_mcontext.gregs[REG_PC];
		uc->uc_mcontext.gregs[REG_PC] = (int)catchNullPointer;
		uc->uc_mcontext.gregs[REG_nPC] = (int)catchNullPointer + 4;
		setcontext(uc);
	    }
	    if ((code & (~0x1fff)) == SparcCHKSTACK ||
		code == SparcCHKSTACKforJNI) {
		uc->uc_mcontext.gregs[REG_O0] = uc->uc_mcontext.gregs[REG_PC];
		uc->uc_mcontext.gregs[REG_PC] = (int)catchStackOverflow;
		uc->uc_mcontext.gregs[REG_nPC] = (int)catchStackOverflow + 4;
		setcontext(uc);
	    }
	}
	break;

    case SIGILL:
	if (si_code == ILL_ILLTRP) {
	    int index;
	    code_t code = *(code_t *)(info->si_addr);

	    if (code == (org_OpenJIT_Sparc_I_TA | Imm(ST_RANGE_CHECK))) {
		uc->uc_mcontext.gregs[REG_O0] = uc->uc_mcontext.gregs[REG_PC];
		uc->uc_mcontext.gregs[REG_O1] = ((int *)(info->si_addr))[1];
		uc->uc_mcontext.gregs[REG_PC] = (int)catchArrayIndexOutOfBounds;
		uc->uc_mcontext.gregs[REG_nPC] = (int)catchArrayIndexOutOfBounds + 4;
		setcontext(uc);
	    }
	    if (code == (org_OpenJIT_Sparc_I_TCC | Imm(ST_RANGE_CHECK))) {
		/* prev_code should be SUBCC */
		code_t prev_code = ((code_t *)(info->si_addr))[-1];
		index = (prev_code >> 14) & 31;
		goto get_register_value;
	    }
	    if (code == (org_OpenJIT_Sparc_I_TLEU | Imm(ST_RANGE_CHECK))) {
		/* prev_code should be SUBCC */
		code_t prev_code = ((code_t *)(info->si_addr))[-1];

		if ((prev_code & Imm(0)) != 0) {
		    index = (signed)(prev_code << 19) >> 19;
		} else {
		    index = prev_code & 31;
		get_register_value:
		    if (index == 0) {
			index = 0;
		    } else if (index <= R_O7) {
			index = uc->uc_mcontext.gregs[REG_G1+index-1];
		    } else {
			int *sp = (int *)(uc->uc_mcontext.gregs[REG_O6]);
			index = sp[index - R_L0];
		    }
		}
		uc->uc_mcontext.gregs[REG_O0] = uc->uc_mcontext.gregs[REG_PC];
		uc->uc_mcontext.gregs[REG_O1] = index;
		uc->uc_mcontext.gregs[REG_PC] = (int)catchArrayIndexOutOfBounds;
		uc->uc_mcontext.gregs[REG_nPC] = (int)catchArrayIndexOutOfBounds + 4;
		setcontext(uc);
	    }
	}
	break;
    }
    return SIGNAL_HANDLER_FAIL;
}

#if JDK_VERSION >= 12
static
void *
OpenJIT_CompiledFrameID(JavaFrame *frame)
{
    return (void *)frame->vars;
}
#endif

static
JavaFrame *
OpenJIT_CompiledFramePrev(JavaFrame *frame, JavaFrame *buf)
{
    struct methodblock *mb;
    uint32_t *fp;

    /* OpenJIT should work outside of application environment. */
    /* So, cease traversing frame at the start of compile. */ 
    if (frame->current_method == OpenJITcompileMB) {
	return NULL;
    }
    if ((uint32_t)frame->vars < 0xe0000000) {
	/* this frame is created by JVM, not JIT */
	/* It's maybe created before compilation in multi-threaded environment */
	return frame->prev;
    }
    mb = frame->current_method;
    if (frame != buf) {
	/* first time */
	memset((char *)buf, 0, sizeof(JavaFrame));
	buf->prev = frame->prev;
	ASM("ta 0x3");		/* flush register windows */
    }
    fp = (uint32_t *)(frame->vars-8);
    if (PC_IN_COMPILED_CODE(fp[15])) {
	fp = (uint32_t *)fp[14];
	buf->current_method = (struct methodblock *)fp[16];
	buf->vars = (stack_item *)(fp + 8);
	return buf;
    } else {
	return frame->prev;
    }
}

#include "org_OpenJIT_Compile.h"

#define RT_INIT(FUNC, ATTR) \
    addr[org_OpenJIT_Compile_RT_##FUNC] = (int)OpenJIT_##FUNC; \
    attr[org_OpenJIT_Compile_RT_##FUNC] = ATTR;

#define VIRTBL		org_OpenJIT_Sparc_RT_ATTR_VIRTBL
#define SET_O0		org_OpenJIT_Sparc_RT_ATTR_PATCH_SET_O0
#define SET_O0_CALL	org_OpenJIT_Sparc_RT_ATTR_PATCH_SET_O0_CALL
#define SET_G3_CALL	org_OpenJIT_Sparc_RT_ATTR_PATCH_SET_G3_CALL

void
OpenJIT_runtime_init(JITlink *linkage, int *addr, int *attr) 
{
    *linkage->p_CompiledCodeSignalHandler = (void *)OpenJIT_SignalHandler;
    *linkage->p_CompiledFramePrev = OpenJIT_CompiledFramePrev;

#if JDK_VERSION >= 12
    *linkage->p_CompiledFrameID = OpenJIT_CompiledFrameID;
    _linkclass_lock = *(linkage->linkclass_lock);
#endif
    classJavaLangObject = *(linkage->classJavaLangObject);
    
    RT_INIT(anewarray, SET_O0_CALL);
    RT_INIT(anewarray_quick, 0);
    RT_INIT(athrow, 0);
    RT_INIT(check_aastore, 0);
    RT_INIT(checkcast, SET_O0_CALL);
    RT_INIT(checkcast_quick, 0);
    RT_INIT(d2l, 0);
    RT_INIT(dcmpg, 0);
    RT_INIT(dcmpl, 0);
    RT_INIT(drem, 0);
    RT_INIT(f2l, 0);
    RT_INIT(fcmpg, 0);
    RT_INIT(fcmpl, 0);
    RT_INIT(frem, 0);
    RT_INIT(getstatic, SET_O0);
    RT_INIT(instanceof, SET_O0_CALL);
    RT_INIT(instanceof_quick, 0);
    RT_INIT(invokeinterface, VIRTBL|SET_G3_CALL);
    RT_INIT(invokeinterface_quick, VIRTBL|SET_G3_CALL);
    RT_INIT(invokespecial, VIRTBL|SET_G3_CALL);
    RT_INIT(invokespecial_resolve, VIRTBL|SET_G3_CALL);
    RT_INIT(invokesuper_quick, VIRTBL|SET_G3_CALL);
    RT_INIT(invokestatic, SET_G3_CALL); /* FIX ME */
    RT_INIT(invokestatic_resolve, SET_G3_CALL); /* FIX ME */
    RT_INIT(invokevirtual_resolve, VIRTBL|SET_G3_CALL);
    RT_INIT(l2d, 0);
    RT_INIT(l2f, 0);
    RT_INIT(lcmp, 0);
    RT_INIT(ldiv, 0);
    RT_INIT(lmul, 0);
    RT_INIT(lrem, 0);
    RT_INIT(lshl, 0);
    RT_INIT(lshr, 0);
    RT_INIT(lushr, 0);
    RT_INIT(monitorEnter, 0);
    RT_INIT(monitorExit, 0);
    RT_INIT(multianewarray, SET_O0_CALL);
    RT_INIT(multianewarray_quick, 0);
    RT_INIT(newarray, 0);
    RT_INIT(new, SET_O0_CALL);
    RT_INIT(new_quick, 0);
    RT_INIT(resolveField, SET_O0);
    RT_INIT(resolveStaticField, SET_O0);
    RT_INIT(resolveString, SET_O0);
}
